using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Reflection;
using System.Collections;

namespace JsonHTTPHandler
{
  /// <summary>
  /// Describes a method call
  /// </summary>
  public class CallInput
  {
    private string serviceName;
    private string methodName;
    private Dictionary<string, MethodParameter> parameters = new Dictionary<string,MethodParameter>();
    private bool isServiceInfoRequest;
    private bool isMethodInfoRequest;
    private bool showJsonResult = false;

    /// <summary>
    /// Dump the output object as formatted HTML
    /// </summary>
    public bool ShowJsonResult
    {
      get { return showJsonResult; }
      set { showJsonResult = value; }
    }

    public bool IsMethodInfoRequest
    {
      get { return isMethodInfoRequest; }
      set { isMethodInfoRequest = value; }
    }

    public bool IsServiceInfoRequest
    {
      get { return isServiceInfoRequest; }
      set { isServiceInfoRequest = value; }
    }
	

    public Dictionary<string, MethodParameter> Parameters
    {
      get { return parameters; }
    }

    public string MethodName
    {
      get { return methodName; }
    }

    public string ServiceName
    {
      get { return serviceName; }
    }

    public object[] Arguments
    {
      get
      {
        return GetMethodArguments();
      }
    }
	
    public CallInput(HttpContext context)
    {
      // Get the service and method names
      string[] snippets = context.Request.Url.ToString().Split('/');
      serviceName = snippets[snippets.Length - 2];
      methodName = snippets[snippets.Length - 1].Split('?')[0];

      // Get the parameters
      for(int index = 0; index < context.Request.QueryString.Count; index++)
      {
        if (!IsSystemOption(context.Request.QueryString.GetKey(index),
          context.Request.QueryString.Get(index)))
        {
          MethodParameter parameter = new MethodParameter();
          parameter.Name = context.Request.QueryString.GetKey(index);
          parameter.Value = context.Request.QueryString.Get(index) == "null" ? null : context.Request.QueryString.Get(index);
          this.parameters.Add(parameter.Name, parameter);
        }
      }
    }

    private object[] GetMethodArguments()
    {
      Dictionary<int, object> arguments = new Dictionary<int, object>();
      foreach (KeyValuePair<string, MethodParameter> pair in this.Parameters)
      {
        MethodParameter p = pair.Value;
        Type paramType = p.ParameterDetails.ParameterType;
        object arg;

        if (paramType.IsEnum)
        {
          arg = Enum.Parse(paramType, p.Value);
        }
        else if (p.ParameterDetails.ParameterType.IsPrimitive)
        {
          arg = GetPrimitiveType(paramType, p.Value);
        }
        else
        {
          arg = GetNormalType(paramType, p.Value);
        }

        arguments.Add(p.ParameterDetails.Position, arg);
      }      

      // Get the arguments in order
      object[] output = new object[arguments.Count];
      for (int index = 0; index < arguments.Count; index++)
      {
        output[index] = arguments[index];
      }
      return output;
    }

    private object GetNormalType(Type paramType, string value)
    {
      object arg = null;

      if(paramType == typeof(string))
      {
        arg = value;
      }
      else if (paramType == typeof(decimal))
      {
        arg = Decimal.Parse(value);
      }
      else if (paramType == typeof(DateTime))
      {
        try
        {
          arg = DateTime.Parse(value);
        }
        catch
        {
          try
          {
            // If the direct parse doesn't work then try parsing the tick value
            arg = new DateTime(Int32.Parse(value));
          }
          catch
          {
          }
        }
      }

      return arg;
    }

    private object GetPrimitiveType(Type type, string value)
    {
      object arg = null;

      // We support  Boolean, Int32,  Double, and Single
      if (type == typeof(Boolean))
      {
        arg = Boolean.Parse(value);
      }
      else if (type == typeof(Int32))
      {
        arg = Int32.Parse(value);
      }
      else if (type == typeof(Double))
      {
        arg = Double.Parse(value);
      }
      else if (type == typeof(Single))
      {
        arg = Single.Parse(value);
      }

      return arg;
    }

    private bool IsSystemOption(string key, string value)
    {
      if (key == null)
      {
        key = value;
      }

      bool systemOption = true;
      switch (key.ToLower())
      {
        case Constants.SHOW_JSON_RESULT:
          this.showJsonResult = true;
          break;
        default:
          systemOption = false;
          break;
      }
      return systemOption;
    }


    public void RemoveUnderscoreParameters()
    {
      List<string> keys = new List<string>();
      foreach (KeyValuePair<string, MethodParameter> pair in this.parameters)
      {
        if (pair.Key.StartsWith("_"))
        {
          keys.Add(pair.Key);
        }
      }
      foreach(string key in keys)
      {
        this.parameters.Remove(key);
      }
    }

    public class MethodParameter
    {
      private string name;
      private string value;
      private ParameterInfo parameterDetails;

      public ParameterInfo ParameterDetails
      {
        get { return parameterDetails; }
        set { parameterDetails = value; }
      }

      public string Value
      {
        get { return value; }
        set { this.value = value; }
      }

      public string Name
      {
        get { return name; }
        set { this.name = value; }
      }

      public MethodParameter(string name, string value)
      {
        this.name = name;
        this.value = value;
      }

      public MethodParameter()
      {
      }
    }
  }
}
